/*
 * Created by JFormDesigner on Thu Oct 13 16:42:25 CEST 2011
 */

package implementation1.gui;

import com.jgoodies.forms.factories.CC;
import com.jgoodies.forms.layout.FormLayout;
import interfaces.data.IHandle;
import interfaces.gui.IShellWindow;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.concurrent.Semaphore;

/**
 * Class, which contains GUI definition of the shell.
 *
 * @author Jan Kolena, Martin Štulc, Jan Šváb
 */
public class ShellWindow extends JFrame implements IShellWindow
{
    /**
     * Login of user, who owns this shell.
     */
    private String loginName;
    /**
     * List of entered commands
     */
    private List<String> commandHistory;
    /**
     * Index of actual command in command's history
     */
    private int actualPosInComHis;
    /**
     * Indicator of disposing window. For handling of situation, when some thread
     * is notify to read input, but the frame is already disposed.
     */
    private boolean bIsDispose;
    /**
     * Contains nextline which will be write into stream
     */
    private String nextline = null;

    private Semaphore mutex;

    /**
     * Constructor of this class.
     *
     * @param handle the reference to handle with name of owner this window and actual selected directory
     */
    public ShellWindow(IHandle handle)
    {
        initComponents();
        setTitle(getTitle() + " - " + handle.getAccount().getNick());
        setPrompt(handle.getActualPath());
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

        bIsDispose = false;
        mutex = new Semaphore(0);

        this.addWindowListener(new WindowAdapter()
        {
            /**
             * Invoked when a window has been closed.
             */
            @Override
            public void windowClosing(WindowEvent e)
            {
                clearCommand();
                nextline = null;

                synchronized (ShellWindow.this)
                {
                    //releasing of the waiting shell on next command
                    mutex.release();
                }

                //command was entered and next command can't be writing into field for commands until
                //shell doesn't request another command.
                text_command.setEditable(false);
            }
        });

        commandHistory = new ArrayList<String>();
        actualPosInComHis = 0;
    }

    /**
     * Gets owner of this window.
     *
     * @return The owner.
     */
    public String getLoginName()
    {
        return loginName;
    }

    /**
     * Sets focus on field for input this window gains focus.
     *
     * @param e
     */
    private void thisWindowGainedFocus(WindowEvent e)
    {
        text_command.requestFocus();
    }

    /**
     * Reaction on pressed key in 'text_command' field.
     *
     * @param e the information about pressed key
     */
    public void text_commandKeyPressed(KeyEvent e)
    {
        KeyEvent keyPressed = e;

        if (keyPressed.getKeyCode() == KeyEvent.VK_DOWN)

        {
            if (commandHistory.isEmpty())
            {
                return;
            }
            actualPosInComHis = (actualPosInComHis + 1) % commandHistory.size();
            while (commandHistory.get(actualPosInComHis).equals(""))
            {
                actualPosInComHis = (actualPosInComHis + 1) % commandHistory.size();
            }
            if ((actualPosInComHis) >= 0 && (actualPosInComHis) < commandHistory.size())
            {

                text_command.setText(commandHistory.get(actualPosInComHis));
            }
        }

        if (keyPressed.getKeyCode() == KeyEvent.VK_UP)

        {
            if (commandHistory.isEmpty())
            {
                return;
            }
            while (commandHistory.get(actualPosInComHis).equals(""))
            {
                actualPosInComHis = (actualPosInComHis + commandHistory.size() - 1) % commandHistory.size();
            }
            if ((actualPosInComHis) >= 0 && (actualPosInComHis) < commandHistory.size())
            {
                text_command.setText(commandHistory.get(actualPosInComHis));
            }
            actualPosInComHis = (actualPosInComHis + commandHistory.size() - 1) % commandHistory.size();
        }

        if (keyPressed.isControlDown() && keyPressed.getKeyCode() == KeyEvent.VK_D)

        {
            keyPressed.consume();
            clearCommand();
            nextline = null;
            write("\n CTRL+D");

            mutex.release();

            //command was entered and next command can't be writing into field for commands as long as
            //shell doesn't request another command.
            text_command.setEditable(false);
        }

        if (keyPressed.getKeyCode() == KeyEvent.VK_ENTER)//enterPressed = true;

        {
            //command was entered and next command can't be writing into field for commands as long as
            //shell doesn't request another command.
            text_command.setEditable(false);
            keyPressed.consume();
            //processing of new input
            String text = text_command.getText();

            clearCommand();

            if (!text.equals(""))
            {
                write("\n > " + text);
                commandHistory.add(text);
                actualPosInComHis = commandHistory.size() - 1;
            }
            else
            {
                write("\n > " + text);
            }

            nextline = text;

            mutex.release();


        }

    }


    private void text_historyMouseReleased(MouseEvent e)
    {
        text_history.setSelectionEnd(text_history.getCaretPosition());
        text_history.setSelectionStart(text_history.getCaretPosition());
    }

    /**
     * Initialization of the frame.
     */
    private void initComponents()
    {
        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
        ResourceBundle bundle = ResourceBundle.getBundle("implementation1.res.gui");
        splitPane1 = new JSplitPane();
        scrollPane3 = new JScrollPane();
        text_history = new JTextArea();
        scrollPane2 = new JScrollPane();
        panel1 = new JPanel();
        label_prompt = new JLabel();
        text_command = new JTextArea();

        //======== this ========
        setMinimumSize(new Dimension(800, 600));
        setTitle(bundle.getString("ShellWindow.this.title"));
        setBackground(Color.black);
        addWindowFocusListener(new WindowAdapter()
        {
            @Override
            public void windowGainedFocus(WindowEvent e)
            {
                thisWindowGainedFocus(e);
            }
        });
        Container contentPane = getContentPane();
        contentPane.setLayout(new BorderLayout());

        //======== splitPane1 ========
        {
            splitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
            splitPane1.setDividerLocation(450);
            splitPane1.setDoubleBuffered(true);
            splitPane1.setDividerSize(5);
            splitPane1.setOneTouchExpandable(true);
            splitPane1.setBackground(Color.black);
            splitPane1.setBorder(null);
            splitPane1.setResizeWeight(1.0);
            splitPane1.setOpaque(true);
            splitPane1.setForeground(Color.black);

            //======== scrollPane3 ========
            {
                scrollPane3.setOpaque(true);

                //---- text_history ----
                text_history.setEditable(false);
                text_history.setText(bundle.getString("ShellWindow.text_history.text"));
                text_history.setLineWrap(true);
                text_history.setFont(new Font("Lucida Console", Font.BOLD, 12));
                text_history.setBackground(Color.black);
                text_history.setForeground(new Color(51, 255, 0));
                text_history.setSelectionColor(Color.black);
                text_history.setSelectedTextColor(new Color(51, 255, 0));
                text_history.setOpaque(true);
                text_history.setBorder(null);
                text_history.addMouseListener(new MouseAdapter()
                {
                    @Override
                    public void mouseReleased(MouseEvent e)
                    {
                        text_historyMouseReleased(e);
                    }
                });
                scrollPane3.setViewportView(text_history);
            }
            splitPane1.setTopComponent(scrollPane3);

            //======== scrollPane2 ========
            {
                scrollPane2.setOpaque(true);

                //======== panel1 ========
                {
                    panel1.setBorder(null);
                    panel1.setBackground(Color.black);
                    panel1.setLayout(new FormLayout(
                            "2dlu, 414dlu:grow",
                            "2dlu, default, fill:default:grow"));

                    //---- label_prompt ----
                    label_prompt.setText(bundle.getString("ShellWindow.label_prompt.text"));
                    label_prompt.setBackground(Color.black);
                    label_prompt.setForeground(new Color(51, 255, 0));
                    label_prompt.setFont(new Font("Lucida Console", Font.BOLD, 14));
                    label_prompt.setOpaque(true);
                    label_prompt.setBorder(null);
                    label_prompt.setLabelFor(text_command);
                    panel1.add(label_prompt, CC.xy(2, 2));

                    //---- text_command ----
                    text_command.setBackground(Color.black);
                    text_command.setForeground(new Color(51, 255, 0));
                    text_command.setBorder(null);
                    text_command.setAutoscrolls(true);
                    text_command.setLineWrap(true);
                    text_command.setFont(new Font("Lucida Console", Font.PLAIN, 14));
                    text_command.setText(bundle.getString("ShellWindow.text_command.text"));
                    text_command.setAlignmentX(2.5F);
                    text_command.setAlignmentY(2.5F);
                    text_command.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
                    text_command.setSelectedTextColor(new Color(51, 255, 0));
                    text_command.setSelectionColor(Color.white);
                    text_command.setOpaque(true);
                    text_command.setMinimumSize(new Dimension(774, 23));
                    text_command.setPreferredSize(new Dimension(774, 30));
                    text_command.setMaximumSize(new Dimension(2147483647, 100));
                    text_command.addKeyListener(new KeyAdapter()
                    {
                        @Override
                        public void keyPressed(KeyEvent e)
                        {
                            text_commandKeyPressed(e);
                        }
                    });
                    /*text_command.addMouseListener(new MouseAdapter()
                    {
                        @Override
                        public void mouseReleased(MouseEvent e)
                        {
                            text_commandMouseReleased(e);
                        }
                    });*/
                    panel1.add(text_command, CC.xy(2, 3));
                }
                scrollPane2.setViewportView(panel1);
            }
            splitPane1.setBottomComponent(scrollPane2);
        }
        contentPane.add(splitPane1, BorderLayout.CENTER);
        setSize(800, 600);
        setLocationRelativeTo(null);
        // JFormDesigner - End of component initialization  //GEN-END:initComponents
    }

    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables
    private JSplitPane splitPane1;
    private JScrollPane scrollPane3;
    private JTextArea text_history;
    private JScrollPane scrollPane2;
    private JPanel panel1;
    private JLabel label_prompt;
    private JTextArea text_command;
    // JFormDesigner - End of variables declaration  //GEN-END:variables

    private int getHistoryLength()
    {
        return text_history.getDocument().getLength();
    }

    private int getCommandLength()
    {
        return text_command.getDocument().getLength();
    }

    /**
     * Appends message into the history pane.
     *
     * @param message The message.
     */
    private void write(String message)
    {
        text_history.append(message);
        text_history.setCaretPosition(getHistoryLength());
    }

    /**
     * Appends message and termines the line into the history pane.
     *
     * @param message The message.
     */
    public synchronized void writeln(String message)
    {
        this.write("\n" + message);
    }

    /**
     * Gets current command. Method suspends the thread, which is trying to read the next command.
     * Waiting thread is notify after entering of new command and pressing key ENTER.
     * WARNING: This method muss be call from another thread.
     *
     * @param moveToHistoryPane Determines, if the command should move to the history pane.
     * @return The current command.
     */
    public String readln(boolean moveToHistoryPane)
    {
        try
        {
            text_command.setEditable(true);
            mutex.acquire();
            //some thread(e.q. shell) requests next command => allowing to user enter next command


        } catch (InterruptedException e)
        {
            return null;
        }

        //if window was dispose meanwhile thread wait on input
        if (bIsDispose)
        {
            return null;
        }

        return nextline;
    }

    /**
     * Clears the command window.
     */
    public void clearCommand()
    {
        text_command.setText("");
    }

    /**
     * Clears the history window.
     */
    public void clearHistory()
    {
        text_history.setText("");
    }

    @Override
    public void close()
    {
        mutex.release();
        synchronized (this)
        {
            this.notifyAll();  //notify all waiting threads on this shell window
        }
        bIsDispose = true;
        dispose();
    }

    /**
     * Set the prompt text. Adds the ">" sign to the end!
     *
     * @param prompt New prompt value.
     */
    public void setPrompt(String prompt)
    {
        this.label_prompt.setText(prompt + ">");
    }
}
